1   /*
2    * Copyright (C) 2006 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.util.concurrent;
18  
19  import junit.framework.TestCase;
20  
21  import java.util.concurrent.Callable;
22  import java.util.concurrent.ExecutorService;
23  import java.util.concurrent.Executors;
24  import java.util.concurrent.TimeUnit;
25  
26  /**
27   * Unit test for {@link SimpleTimeLimiter}.
28   *
29   * @author kevinb
30   */
31  public class SimpleTimeLimiterTest extends TestCase {
32  
33    private static final int DELAY_MS = 50;
34    private static final int ENOUGH_MS = 500;
35    private static final int NOT_ENOUGH_MS = 5;
36  
37    private TimeLimiter service;
38  
39    private static final ExecutorService executor
40        = Executors.newFixedThreadPool(1);
41  
42    private static String someGoodStaticMethod() throws InterruptedException {
43      TimeUnit.MILLISECONDS.sleep(DELAY_MS);
44      return "yes";
45    }
46  
47    private static String someBadStaticMethod() throws InterruptedException,
48        SampleException {
49      TimeUnit.MILLISECONDS.sleep(DELAY_MS);
50      throw new SampleException();
51    }
52  
53    @Override protected void setUp() throws Exception {
54      super.setUp();
55      service = new SimpleTimeLimiter(executor);
56    }
57  
58    public void testGoodCallableWithEnoughTime() throws Exception {
59      long start = System.nanoTime();
60      String result = service.callWithTimeout(
61          new Callable<String>() {
62            @Override
63            public String call() throws InterruptedException {
64              return someGoodStaticMethod();
65            }
66          }, ENOUGH_MS, TimeUnit.MILLISECONDS, true);
67      assertEquals("yes", result);
68      assertTheCallTookBetween(start, DELAY_MS, ENOUGH_MS);
69    }
70  
71    public void testGoodCallableWithNotEnoughTime() throws Exception {
72      long start = System.nanoTime();
73      try {
74        service.callWithTimeout(
75            new Callable<String>() {
76              @Override
77              public String call() throws InterruptedException {
78                return someGoodStaticMethod();
79              }
80            }, NOT_ENOUGH_MS, TimeUnit.MILLISECONDS, true);
81        fail("no exception thrown");
82      } catch (UncheckedTimeoutException expected) {
83      }
84      assertTheCallTookBetween(start, NOT_ENOUGH_MS, DELAY_MS);
85    }
86  
87    public void testBadCallableWithEnoughTime() throws Exception {
88      long start = System.nanoTime();
89      try {
90        service.callWithTimeout(
91            new Callable<String>() {
92              @Override
93              public String call() throws SampleException, InterruptedException {
94                return someBadStaticMethod();
95              }
96            }, ENOUGH_MS, TimeUnit.MILLISECONDS, true);
97        fail("no exception thrown");
98      } catch (SampleException expected) {
99      }
100     assertTheCallTookBetween(start, DELAY_MS, ENOUGH_MS);
101   }
102 
103   public void testBadCallableWithNotEnoughTime() throws Exception {
104     long start = System.nanoTime();
105     try {
106       service.callWithTimeout(
107           new Callable<String>() {
108             @Override
109             public String call() throws SampleException, InterruptedException {
110               return someBadStaticMethod();
111             }
112           }, NOT_ENOUGH_MS, TimeUnit.MILLISECONDS, true);
113       fail("no exception thrown");
114     } catch (UncheckedTimeoutException expected) {
115     }
116     assertTheCallTookBetween(start, NOT_ENOUGH_MS, DELAY_MS);
117   }
118 
119   public void testGoodMethodWithEnoughTime() throws Exception {
120     SampleImpl target = new SampleImpl();
121     Sample proxy = service.newProxy(
122         target, Sample.class, ENOUGH_MS, TimeUnit.MILLISECONDS);
123     long start = System.nanoTime();
124     assertEquals("x", proxy.sleepThenReturnInput("x"));
125     assertTheCallTookBetween(start, DELAY_MS, ENOUGH_MS);
126     assertTrue(target.finished);
127   }
128 
129   public void testGoodMethodWithNotEnoughTime() throws Exception {
130     SampleImpl target = new SampleImpl();
131     Sample proxy = service.newProxy(
132         target, Sample.class, NOT_ENOUGH_MS, TimeUnit.MILLISECONDS);
133     long start = System.nanoTime();
134     try {
135       proxy.sleepThenReturnInput("x");
136       fail("no exception thrown");
137     } catch (UncheckedTimeoutException expected) {
138     }
139     assertTheCallTookBetween(start, NOT_ENOUGH_MS, DELAY_MS);
140 
141     // Is it still computing away anyway?
142     assertFalse(target.finished);
143     TimeUnit.MILLISECONDS.sleep(ENOUGH_MS);
144     assertFalse(target.finished);
145   }
146 
147   public void testBadMethodWithEnoughTime() throws Exception {
148     SampleImpl target = new SampleImpl();
149     Sample proxy = service.newProxy(
150         target, Sample.class, ENOUGH_MS, TimeUnit.MILLISECONDS);
151     long start = System.nanoTime();
152     try {
153       proxy.sleepThenThrowException();
154       fail("no exception thrown");
155     } catch (SampleException expected) {
156     }
157     assertTheCallTookBetween(start, DELAY_MS, ENOUGH_MS);
158   }
159 
160   public void testBadMethodWithNotEnoughTime() throws Exception {
161     SampleImpl target = new SampleImpl();
162     Sample proxy = service.newProxy(
163         target, Sample.class, NOT_ENOUGH_MS, TimeUnit.MILLISECONDS);
164     long start = System.nanoTime();
165     try {
166       proxy.sleepThenThrowException();
167       fail("no exception thrown");
168     } catch (UncheckedTimeoutException expected) {
169     }
170     assertTheCallTookBetween(start, NOT_ENOUGH_MS, DELAY_MS);
171   }
172 
173   private static void assertTheCallTookBetween(
174       long startNanos, int atLeastMillis, int atMostMillis) {
175     long nanos = System.nanoTime() - startNanos;
176     assertTrue(nanos >= atLeastMillis * 1000000);
177     assertTrue(nanos <= atMostMillis * 1000000);
178   }
179 
180   public interface Sample {
181     String sleepThenReturnInput(String input);
182     void sleepThenThrowException() throws SampleException;
183   }
184 
185   @SuppressWarnings("serial")
186   public static class SampleException extends Exception {}
187 
188   public static class SampleImpl implements Sample {
189     boolean finished;
190 
191     @Override
192     public String sleepThenReturnInput(String input) {
193       try {
194         TimeUnit.MILLISECONDS.sleep(DELAY_MS);
195         finished = true;
196         return input;
197       } catch (InterruptedException e) {
198         return null;
199       }
200     }
201     @Override
202     public void sleepThenThrowException() throws SampleException {
203       try {
204         TimeUnit.MILLISECONDS.sleep(DELAY_MS);
205       } catch (InterruptedException e) {
206       }
207       throw new SampleException();
208     }
209   }
210 }